import { app } from "../../scripts/app.js"
import { ComfyWidgets } from "../../scripts/widgets.js"

const hardLimit = 100

// Does most of the regular textarea cleanup, but leaks a ref in domWidget.js -> elementWidgets until the node is removed
function removeWidget(node, widget) {
  widget.onRemove?.()
  const index = node.widgets.indexOf(widget)
  if (index !== -1)
    node.widgets.splice(index, 1)
}

function removeAllWidgets(node) {
  for (const widget of node.widgets) {
    widget.onRemove?.()
  }
  node.widgets.length = 0
}

// Main logic
app.registerExtension({
  name: "WTFDebugNode",
  async beforeRegisterNodeDef(nodeType, nodeData, app) {
    if (nodeData.name !== "WTFDebugNode") return

    // Ensure attached links keep their colours
    const origOnConnectionsChange = nodeType.prototype.onConnectionsChange
    nodeType.prototype.onConnectionsChange = function (type, slotIndex, isConnected, link, ioSlot) {
      origOnConnectionsChange?.apply(this, arguments)

      if (isConnected && link?.type === "*")
        link.type = this.graph.getNodeById(link.origin_id)?.outputs?.[link.origin_slot]?.type
    }

    // Show the text in the UI
    const origOnExecuted = nodeType.prototype.onExecuted
    nodeType.prototype.onExecuted = function (message) {
      const r = origOnExecuted
        ? origOnExecuted?.apply(this, arguments)
        : undefined

      // Because debug
      console.log(message)

      // Sanity init
      this.widgets ??= []
      this.serialize_widgets = false
      const baseOpacity = app.canvas.editor_alpha

      // Clear if invalid
      const itemsLength = parseInt(message?.items?.length)
      if (isNaN(itemsLength)) {
        removeAllWidgets(this)
        return r
      }

      const isMultiItem = itemsLength > 1
      const wasMultiItem = this.debugNodeLength !== 1

      // Clean up existing widgets if required
      if (isMultiItem !== wasMultiItem) {
        removeAllWidgets(this)
      }
      // Remove only widgets we can't reuse
      else if (this.debugNodeLength > itemsLength) {
        for (let i = itemsLength; i <= this.debugNodeLength; i++) {
          for (const widget of this.widgets.filter(x => x.name.endsWith(` ${i}`))) {
            removeWidget(this, widget)
          }
        }
      }

      this.debugNodeLength = itemsLength
      const maxItems = Math.min(itemsLength, hardLimit)

      // Create / update widget values for each item
      for (let i = 0; i < maxItems; i++) {
        const item = message.items[i]
        const { type, len, shape, firstIterItem, value } = item

        function processWidget(node, name, value, isMultiline = false) {
          const numberedName = isMultiItem
            ? `${name} ${i}`
            : name
          const comfyType = isMultiline
            ? ["STRING", { multiline: true }]
            : ["STRING", {}]

          const current = node.widgets.find(x => x.name === numberedName)
          const widget = current ?? ComfyWidgets.STRING(node, numberedName, comfyType, app).widget

          widget.options ??= {}
          widget.options.serialize = false
          widget.value = value
          widget.disabled = value == null
          if (isMultiline) {
            widget.element.style.opacity = widget.disabled
              ? baseOpacity * 0.5
              : baseOpacity
          }
        }

        processWidget(this, "type", type)
        processWidget(this, "len()", len)
        processWidget(this, "shape", shape)
        processWidget(this, "type of first iter() item", firstIterItem)
        processWidget(this, "value", value, true)
      }

      requestAnimationFrame(() => {
        this.onResize?.(this.size)
      })

      return r
    }
  },
})
